All right.
So in this next section of the course we're going to take a dive into type annotation.
If you've been in my beginner's course, you'll know that type annotation is a way for us to specify
the type of a variable, a parameter or return value of a function, and it helps the Lua compiler perform
type checking when the script gets compiled, it can help us understand our code easier as well as for
other developers, and it can also highlight problems to us when functions we create are not being passed.
The correct arguments.
Type annotations also allow us to use auto completion for when accessing properties of a particular
type, when the compiler doesn't know a type of a variable, but we do.
We can declare the type of a variable in Lua you using a colon, and then write in the name of the type.
We can do this for all of the primitive types in Lua such as booleans, numbers, nil strings, and
so on.
So if I were to create a variable, I can call it anything I want.
I'll just call it some var and I can use a colon right after the name.
And from here I can define the type that this variable is going to be such as number.
And in here it'll show us all the types that we can set for this variable with this new text.
So we're going to set it to number.
And now when I go to type out this variable you'll see here right on the right, it tells us that the
type of this variable is type of number.
So I can set this to anything I'd like such as 123.
And we have no problem here inside of studio.
Now this is where some modes come into play.
So the compiler does something called type inferencing.
Or it can infer the type of a variable or a parameter, or a return of a function by itself based on
what values we assign to it.
So by default, Lua already knows that this type of the variable is going to be a number because we're
assigning it a number.
So if I got rid of this type declaration here and I want to type in some var c, it already says to
us that it's a number because we assigned it a number.
But if we didn't initialize this variable, if we go and type it out again, you'll see it says A which
stands for any.
So that means the compiler is saying hey, this variable hasn't been instantiated.
We don't know what's going to be stored in it.
So it can store anything, but we can override it and say maybe this variable is going to store maybe
a player.
And now when we go and type out some var, it will tell us, hey, this is going to be a player data
type for this variable.
Now this inferencing can change based on the modes that we select for the script.
So there's three modes.
There's one called no check.
There's one called non strict and there's one called strict.
Now no check means that the inference mode is not going to be enabled at all.
The inference engine in Roblox studio won't be enabled whatsoever.
So it's not going to go ahead and infer any of the types for the variables.
Usually I can't really think of a use case for no check, but we're going to go ahead and move on to
non strict.
Now this is the type inferencing mode that is enabled by default.
And the way you enable a type inference mode is that at the top of your script before you write down
any code, you can create a comment and use an exclamation mark and type the name of the mode you want
for the type annotation, such as no check or you can do non strict, which would make sense because
it's enabled by default and then the other one would be strict.
So this is telling the compiler hey this is what kind of inferencing mode we want you to have for the
variables in our script.
So we're going to keep it on strict that way we can see some examples later on.
And strict is basically the same thing as non strict.
And what non strict does is that it will infer basically any variable parameter or return from a function
that it can't figure out to any.
So for example here, this variable I didn't instantiate, it's automatically set to any right because
it doesn't know.
It's inferring that it could be anything at all.
That's what the non strict mode does.
Now what the strict mode does is that this inference mode um, it will throw out an error if the number
of parameters that you pass to a function is inappropriate.
Basically, if you pass more parameters to a function that's not specified or you don't pass enough
parameters to a function, then it's going to throw out an error.
It's going to highlight the script and say, hey, you don't have enough arguments being supplied to
a function.
So let's go ahead and create some variable examples for each one of the primitive types.
So for one I could call it some bool.
And we can set it to be a boolean just like this.
And now studio sees it as a boolean.
We can do the same thing for a number.
So we could do some number and set the type to be number.
So again some number.
It's telling us the type is of a number.
We can do nil.
So we could do some nil and set it to nil.
And when you go and check here it tells us that the type is going to be nil.
You can do the same thing for strings.
So some string.
Declare that this type will be of string.
And you go here, you look for some string and there we go tells us it's a string.
And then for functions it's going to be a little bit different.
So for function you can't do function.
It doesn't work like that.
What you actually have to do is you have to put two colons.
And then after these two colons you do a dash and an arrow to tell you what this function is going to
return.
And I'm just going to set it to nil.
So what this is declaring here is it's telling us that this variable is going to store a function that
takes no parameters and it returns nil.
So if I go to some function, it tells us that some function that takes no parameters and returns nil.
Now I could do anything here.
I could say, hey, maybe this function returns a player and we go and check some function.
Now it's telling us it returns a player.
And then I can also declare parameters in here.
So let's say some param.
And this parameter needs to be I don't know a number.
When we go and type out some function again it tells us that this function takes a parameter which has
to be a number and it returns to us a player.
So this is how you declare the type for a variable that is going to be a function.
And then for a table, it's pretty simple.
We can do some table, and all we need to do is put two curly brackets.
So this is denoting that some table is a table.
And currently there is nothing in it.
But if we want to define some, you know, thing that this variable can only contain, like it has to
have some set of properties in the table or key value pairs.
We could do something like uh, some key and this key has to contain maybe a boolean.
Right.
So if we go and look at some table again.
It's telling us that there is some key in here, and it has to be a buoyant.
Now, if I go ahead and try to assign a table to this variable, this table has to contain this some
key and it has to store a boolean.
Otherwise you'll see here table type some table not compatible with this type because it's missing the
field some key.
So I actually have to put a field in here called some key and assign it a boolean value like false.
And you see we don't get that red underline anymore.
But if I set it to something like one, two, three we're going to get an error because we cannot convert
a number into a boolean which is required right here.
Right.
We're telling the compiler or telling studio that this key right here has to be a boolean.
If we assign it to anything other than a boolean, it's going to give us this red underline and tell
us that there is a problem going on.
So this right here.
This is not okay.
You don't want to do this.
Instead, we want to make sure we set some key to a boolean.
And we're all good to go.
No.
Red underline.
Now let's go ahead and take a look at some Roblox data types.
So if I create a new part using the instance dot new function part.
What you're going to see here is that Roblox studio automatically knows that this variable is going
to contain the type part, because the instance dot new function, when given this part string, returns
a part back to be stored in this variable, so it automatically knows that it is of the type part.
So there's no need to go ahead and put part here.
It already knows that.
So let's do like a small little example with a function I'm going to create a function.
I'll call it ad numb's.
So it's going to add two numbers together.
And we're going to take two parameters one number and a second number.
So we can call one number a and the other number b.
And I create my function right here.
Now, when I go to call this function, it just tells us that A is a, B is B.
It's not telling us exactly what type these variables have to be.
It's basically telling us we can pass whatever we want.
But when we want to make sure to pass only a specific type to this function, then we can go ahead and
go to where these parameters are declared and put a colon.
And we have to say, okay, a is going to be a number.
And B also has to be a number.
And when we go to call the Adnams function, it tells us a has to be a number, B has to be a number.
And if we try to pass anything else like a boolean, like false or a string, uh, hello again.
It's going to underline and tell us that we can't convert type of string to a number, or we can't convert
type of boolean to number.
It's telling us, hey, you can only pass numbers to this function.
Now what if I also wanted to define what this function returns?
Well that's also really simple.
We can put a colon at the end of this parentheses.
And then we can tell exactly what this function is going to return.
So what is it going to return.
Well it's going to return a sum of these two numbers.
So we'll just return a number.
And then when we go and call this function again.
It's telling us it takes a number for a, takes a number for B, and it returns to us a number.
So this is how we can declare what types are going to be returned or given to a function.
And then from this point we can just do return a plus B.
And because we're returning two numbers, in fact, we actually don't even need to declare what this
function returns, because if we store what this function returns.
So we'll create a variable, call it some num and equal to add numb's we'll pass a number here and a
number here.
We'll go to type out some num.
Of course it's going to tell us it's a number.
But even if we remove this here and we type out some num some num, it still tells us it's a number
because we're passing in a number to this function.
And all this function is doing is adding those two numbers and returning it.
So the compiler or studio automatically sees, hey, this is returning a number, no big deal.
However, there may be cases where we're going to have a lot more complex number, and studio is not
going to be able to figure out what type is being returned from a function, which is why it's sometimes
good to declare what a function returns.
Again, it makes using your functions a lot easier because you know what it returns.
You know what it takes, and then it also helps us out when we go to type out the name of this variable
and it gives us oops, we get an error type because we, uh, we didn't finish out.
This function.
So again we'll do one two.
When we type out the name it tells us the number.
And we can access all the properties for this variable in case it was like a Roblox data type.
Now another topic I want to take a look at is optional parameters for a function.
So let's say I don't really actually need to supply B to this function.
I can only take a, and if B doesn't exist, I can set it to a default value.
So what I could do is I could set B equal to itself, or if B was never passed to the function, I could
set it to a number like one.
And this is going to be our default.
Right.
And then to declare B as optional, I can just put a question mark after the number.
And now when I go and supply just the number one to this function, we're not going to have an error
here.
However, if I got rid of this question mark, we're going to get this underline and say we did not.
We didn't pass enough arguments to this function.
We need to pass another one.
But if I want B to be optional I just added a question mark and the problem is solved.
Now you're going to see that B gets underlined and it's telling us type number.
Question mark cannot be converted into number.
And the issue here is that studio just isn't smart enough to figure out that we've actually set B to
a default, which is a number.
So if you don't like this red underline here, what you could do is you could create an if statement
that does something like this.
So if B then you could do return a and B and you see that red underline disappears because studio might
think that B could possibly be nil, which is why it gives that underline there.
But when we do it this way then you see we'll have another underline because we're not returning a number
here, because if B doesn't exist then the function returns nothing.
But that doesn't happen because we set a default here.
But again, the compiler or studio isn't smart enough to figure that out.
So you're just getting these weird underlines.
So I wouldn't really worry about it too much.
Just return a plus B, you'll just get this little red underline.
Don't worry about it.
But let's move on and let's create another function I'm going to call this function uh I don't know
get random instance.
And we could just say, oh, you know, this, uh, we need to pass a player to this function.
Right.
So we'll have a variable just called player.
And this variable has to be a player.
And we could declare that this function returns, I don't know, maybe a part.
Right.
So we get a part, set it to part.
And this function, you know, does whatever, blah blah blah.
And it can return to us, um, some part.
So some part will create a variable in here.
And perhaps it equals to like instance that new part or something like that.
So whenever we get something returned from this function.
So some part equal to get random instance, we could pass um instance dot new player just for this example.
Some part is going to be a part.
So some part there we go tells us it is of type part.
If we got rid of this I'm pretty sure it will say type part.
There we go.
Because it knows that the instance dot new function returns a part.
It sets that type to this variable, and then that variable simply gets returned and stored in here.
So that's why it still says it is of the type part.
It's kind of hard to give an example of when you'll have a complex function that the compiler won't
be able to interpret what type it returns, but we're going to be using type annotations and type inferencing
later in the course on our projects.
So it's just good practice to declare what a function returns, which is not a player, it's a part.
And I can show you another example of something that's not okay.
Let's say I had another number like some number two, and I say it's a boolean, but I set it equal
to the Adnams function and pass like one and three.
Again we're going to get a red underline.
It says type number could not be converted into boolean because this function is returning a variable
of type number.
And you can't store in a variable of a type boolean.
Right.
So this this is not okay.
Don't do this.
This is okay.
This is not okay.
So this would fall under inferring the returns of functions.
So again.
Studio knows that some num is a number.
Even if we do not declare that it returns a number, it's simply inferring that based on what gets supplied
to the function and what gets returned from the function.
So this is the compiler inferring to us what the type of this variable is going to be.
One last thing I want to touch on real quick is of course auto completion.
So if we had a function that does something, does something.
And it requires a parameter.
We'll just call it some param.
And we know that we need to supply like a, perhaps a part.
We have to supply a part to this function.
But of course the compiler doesn't know that some param is going to be a part.
So when we index it it doesn't really know what we're indexing.
Right.
But if we go in here and we set it to be of type part and we go and index it, then we get the autocompletion
and we get all of the properties, the events and all this good stuff that is a part of a part.
So it makes it really easy for us to use Autocompletion or access any types of events or functions because
they can get auto filled for us.
So when we type P set to parent, we can tab to autocomplete and it helps to make us more efficient.
So again the purpose of type inferencing is to make your scripts easier to understand for you and other
developers.
It helps you use Autocompletion as you've seen here, and it also gives you a great understanding of
what exactly the types of variables are that you're working with.
So when you have a bunch of variables in a script and you have to use them a bunch in your code, it's
going to help a lot.
When you can type out that variable name, it tells you exactly the type of that variable, and you
can go in and access all the properties you need for that specific variable based on its type.
So this was a lot of information to digest.
But we're going to continue working with types in this section.
And I'll see you in the next lecture.